#ifndef FS_FS_H
#define FS_FS_H

#define SECTORSIZE 0x0200
#define BLOCKSIZE (SECTORSIZE * 2)        // 磁盘块大小 1KB
#define SIZE 0x00100000         // 虚拟磁盘空间大小 1MB
#define END 0xFFFF              // FAT中的文件结束标志
#define FREE 0x0000             // FAT中盘块空闲标志
#define MAXOPENFILE 10          // 最多同时打开文件个数
#define MAX_FILE_SIZE 0x2000    // 最大文件大小8KB（即使FCB中的length是32位）

#define FAT_SIZE 2              // FAT大小
#define FAT1_START_BLOCK 1                              // FAT1 起始盘块号
#define FAT2_START_BLOCK (FAT1_START_BLOCK + FAT_SIZE)  // FAT2 起始盘块号
#define ROOT_START_BLOCK (FAT2_START_BLOCK + FAT_SIZE)  // 根目录区起始盘块号

#define NEW(type) ((type*) malloc(sizeof(type)))    // 分配一组空间

#define META_ADDR(vdisk) ((META*)(vdisk))                                           // 第1个block放引导块
#define FAT1_ADDR(vdisk) ((FAT*)((vdisk) + BLOCKSIZE * FAT1_START_BLOCK))           // 第2、3个block放FAT1
#define FAT2_ADDR(vdisk) ((FAT*)((vdisk) + BLOCKSIZE * FAT2_START_BLOCK))           // 第4、5个block放FAT1
#define ROOT_ADDR(vdisk) ((FCB*)((vdisk) + BLOCKSIZE * ROOT_START_BLOCK))           // 第6、7个block放根目录文件
#define FILE_START_ADDR(vdisk) (META_ADDR(vdisk)->startblock)

#define BUILD_TIME(tm) ((((tm)->tm_hour) * 2048) + (((tm)->tm_min) * 32) + (((tm)->tm_sec) / 2))
#define BUILD_DATE(tm) (((((tm)->tm_year) - 80) * 512) + ((((tm)->tm_mon) + 1) * 32) + (((tm)->tm_mday)))

#define PATH_SPLIT "/"
#define FNAME_SPLIT "."

typedef unsigned char byte;
typedef unsigned short dbyte;
typedef unsigned int qbyte;

typedef int (*builtin_func_t)(char *);

typedef struct {
    char filename[8];       // 文件名
    char extname[3];        // 文件扩展名
    byte flag;              // 文件属性字段，值为0时表示目录文件，值为1时表示数据文件
    dbyte time;             // 文件创建时间
    dbyte date;             // 文件创建日期
    dbyte start;            // 文件起始盘块号
    int length;             // 文件长度
    byte free;              // 表示目录项是否为空，若值为0，表示空，值为1，表示已分配
    byte reserve[9];        // 保留字
}__attribute__((packed)) FCB; // 按字节对齐，大小32Byte

typedef struct {
    dbyte id;               // 磁盘块的状态（空闲的，最后的，下一个）
} FAT;

typedef struct {
    FCB fcb;                // 打开文件的FCB内容
    int dirno;              // 相应打开文件的目录项在父目录文件中的盘块号
    int diroff;             // 相应打开文件的目录项在父目录文件的dirno盘块中的目录项序号
    char dir[80];           // 相应打开文件所在的目录名，这样方便快速检查出指定文件是否已经打开
    byte ptr;               // 读写指针在文件中的位置
    int parent;             // 父目录文件在open_list中的下标
    byte fcbstate;          // 是否修改了文件的FCB的内容，如果修改了置为1，否则为0
    byte topenfile;         // 表示该用户打开表项是否为空，若值为0，表示为空，否则表示已被某打开文件占据
} USEROPEN;

typedef struct {
    char info[32];          // 存储一些描述信息
    char oem[32];           // 存储OEM信息
    dbyte sector_size;      // 扇区大小
    qbyte block_num;        // 块数量
    dbyte block_size;       // 块大小
    dbyte maxopenfile;      // 最多打开文件数量
    dbyte root;             // 根目录文件的起始盘块号
    byte *startblock;       // 虚拟磁盘上数据区开始位置
} META;

// 函数声明
/**
 * 深拷贝FCB
 * @param dst 目的FCB
 * @param src 源FCB
 */
void fcbcpy(FCB *dst, const FCB *src);

/**
 * 打开一个文件并将其登记于open_list数组中
 * @param fname 要打开的文件名
 * @param type 0代表打开目录文件，1代表打开数据文件
 * @return 打开后的项目在open_list数组中的index，出错返回-1
 */
int open(char *fname, int type);

/**
 * 关闭一个文件并删除其在open_list数组中的记录
 * @param index 要关闭的文件在open_list中的下标
 * @return 被关闭的文件的父目录文件在open_list中的下标，出错返回-1
 */
int close(int index);

/**
 * 初始化文件系统，分配空间
 */
void init();

/**
 * 打开文件系统，若打不开或文件系统不存在则进行格式化
 * @param fs 要打开的文件系统的路径名
 */
void startup(char *fs);

/**
 * 对磁盘里的文件系统进行格式化
 */
void format();

/**
 * 切换工作路径到dirs
 * @param dirs 要切换过去的路径
 * @return 成功返回0，失败返回-1
 */
int cd(char *dirs);

/**
 * 从已经打开的文件open_list[i]中读取len字节的内容放入msg中
 * @param i 进行读取的已打开的文件
 * @param msg 读取出来的内容存放的缓冲区
 * @param len 要读取的字节数
 * @return 成功读取的字节数
 */
int do_read(int i, byte *msg, int len);

/**
 * 将len字节msg中的内容写到已经打开的文件open_list[i]中
 * @param i 进行写入的已打开文件
 * @param msg 要写入的内容
 * @param len 要写入的字节数
 * @param type 写入的类型：0代表从头覆盖，1代表从读写指针处开始写，2代表从文件末尾追加写
 * @return 正常写入返回0，出错返回-1
 */
int do_write(int i, byte *msg, int len, int type);

/**
 * 从FAT表里获取一个空闲的盘块的block号
 * @return 返回找到的空闲盘块号，找不到返回END
 */
dbyte get_free_block();

/**
 * 从open_list数组中寻找可供存放即将打开的文件的项，找不到则代表目前已经打开了10个文件。
 * @return 返回找到的下标，找不到返回-1
 */
int get_free_open_list();

void ls();

int create(char *filename);

void rm(char *filename);

#endif //FS_FS_H
